package io.renren.modules.oss.controller;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import io.renren.common.exception.RRException;
import io.renren.common.utils.ConfigConstant;
import io.renren.common.utils.R;
import io.renren.modules.oss.cloud.AliyunCloudStorageService;
import io.renren.modules.oss.cloud.CloudStorageConfig;
import io.renren.modules.oss.cloud.OSSFactory;
import io.renren.modules.sys.service.SysConfigService;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;

/**
 * @author Chenjiacheng
 * @CREATE 20181202 22:32
 */
@RestController
@RequestMapping("/open/oss")
public class ApiOssController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private SysConfigService sysConfigService;

    // 返回上传Policy和回调设置代码
    @GetMapping("/policy/get")
    public R getPolicy(  ){
        logger.debug("获取上传Policy和回调设置代码");
        CloudStorageConfig config = sysConfigService.getConfigObject( ConfigConstant.CLOUD_STORAGE_CONFIG_KEY, CloudStorageConfig.class);
        AliyunCloudStorageService storageService = (AliyunCloudStorageService)(OSSFactory.build());
        OSSClient client = storageService.getClient();
        long expireTime = 600;
        long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
        Date expiration = new Date(expireEndTime);
        PolicyConditions policyConds = new PolicyConditions();
        policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
        policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, config.getAliyunDir());

        String host = "http://" + config.getAliyunBucketName() + "." + config.getAliyunEndPoint();

        try {
            String postPolicy = client.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = client.calculatePostSignature(postPolicy);

            R respMap = R.ok();
            respMap.put("accessId", config.getAliyunAccessKeyId());
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", config.getAliyunDir());
            respMap.put("host", host );
            respMap.put("expire", String.valueOf(expireEndTime / 1000));


//            // 构建自己的参数
//            String myVar = "${x:var1:"+ albumId +"}";
            /*
            JSONObject jasonCallback = new JSONObject();
            //jasonCallback.put("callbackUrl", setting.getAliyunCallbackUrl());
            jasonCallback.put("callbackBody",
                    "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
            //jasonCallback.put("callbackBodyType", "application/json");
            jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
            String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
            respMap.put("callback", base64CallbackBody);
            */
            return respMap;

        }catch ( Exception e ){
            e.printStackTrace();
            throw new RRException( e.getMessage() );
        }
    }

    // oss服务器回调接口
    @PostMapping("/callback")
    public R callback(HttpServletRequest request){
        System.out.println( "oss服务器回调接口" );
        logger.info("request.getQueryString(): {}", request.getQueryString());
        // 验证是否为oss服务器的请求
        try {
            String ossCallbackBody = GetPostBody( request.getInputStream(), Integer.parseInt(request.getHeader("content-length")));

            boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
            logger.info("verify result:" + ret);
            logger.info("OSS Callback Body:" + ossCallbackBody);
            if (ret) {
                //response(request, response, "{\"Status\":\"OK\"}", HttpServletResponse.SC_OK);
                return R.ok().put("Status","OK");
            } else {
                //response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_BAD_REQUEST);
                return R.ok().put("Status","verdify not ok");
            }
        }catch ( Exception e ){
            e.printStackTrace();
            logger.error( "验证是否为oss服务器的请求, errorMsg:{}", e.getMessage() );
            return R.ok().put("Status","verdify not ok");
        }
    }

    protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody) throws NumberFormatException, IOException
    {
        boolean ret = false;
        String autorizationInput = new String(request.getHeader("Authorization"));
        String pubKeyInput = request.getHeader("x-oss-pub-key-url");
        byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/"))
        {
            logger.error("pub key addr must be oss addrss");
            return false;
        }
        String retString = executeGet(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");
        String queryString = request.getQueryString();
        String uri = request.getRequestURI();
        String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
        String authStr = decodeUri;
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        logger.debug( " authStr : {}", authStr );
        authStr += "\n" + ossCallbackBody;
        ret = doCheck(authStr, authorization, retString);
        return ret;
    }

    public String GetPostBody(InputStream is, int contentLen) {
        if (contentLen > 0) {
            int readLen = 0;
            int readLengthThisTime = 0;
            byte[] message = new byte[contentLen];
            try {
                while (readLen != contentLen) {
                    readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                    if (readLengthThisTime == -1) {// Should not happen.
                        break;
                    }
                    readLen += readLengthThisTime;
                }
                return new String(message);
            } catch (IOException e) {
            }
        }
        return "";
    }

    @SuppressWarnings({ "finally" })
    public String executeGet(String url) {
        BufferedReader in = null;

        String content = null;
        try {
            // 定义HttpClient
            @SuppressWarnings("resource")
            DefaultHttpClient client = new DefaultHttpClient();
            // 实例化HTTP方法
            HttpGet request = new HttpGet();
            request.setURI(new URI(url));
            HttpResponse response = client.execute(request);

            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
            in.close();
            content = sb.toString();
        } catch (Exception e) {
        } finally {
            if (in != null) {
                try {
                    in.close();// 最后要关闭BufferedReader
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return content;
        }
    }

    public static boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            boolean verify = signature.verify(sign);
            return verify;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }
}
